﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Media.Animation;
using CommonServices;
using ContribServices;
using AutoClick.WPFHelpers;

namespace AutoClick.Algorithms
{
    /// <summary>
    /// Algorithm which lauch a click if the pointer is in the radius and if this pointer mostly stays in the
    /// radius between two periods of potential auto-click.
    /// This algorithm is reliable only with EyeTracking because pointer always must moving.
    /// </summary>
    class AlgorithmPercentageCalculating : AutoClickAlgorithmBase, IAutoClickAlgorithm
    {
        #region Variables
        private string _typeOfClick;
        private bool _clickInProcess;
        private double _pointerDistanceFromReferance;
        private int _areaOfReferenceClickRadius;
        private int _accuracyOfPointerMovements;
        private int _numberOfPointerPositionsOutOfAreaClick;
        private int _countNumberOfCommonTimerTicks;
        private int _commonTimerMultiple;

        private List<int> _listOfPointerPositionOutOfAreaClick;
        private Point _referencialPointerPosition;
        private Point _distanceBetweenPointerAndReference;
        private AutoClickCircleControl _autoClickCircleControl;
        private ICommonTimer _commonTimer;
        private IPointerDeviceDriver _pointerDeviceDriver;

        public Point ReferencialPointerPosition
        {
            get { return _referencialPointerPosition; }
            set { _referencialPointerPosition = value; }
        }
        public AutoClickCircleControl AutoClickCircleControl
        {
            get { return _autoClickCircleControl; }
            set { _autoClickCircleControl = value; }
        }
        public bool ClickInProcess
        {
            get { return _clickInProcess; }
            set { _clickInProcess = value; }
        }
        public int CountNumberOfCommonTimerTicks
        {
            get { return _countNumberOfCommonTimerTicks; }
            set { _countNumberOfCommonTimerTicks = value; }
        }
        public int AreaOfReferenceClickRadius
        {
            get { return _areaOfReferenceClickRadius; }
            set { _areaOfReferenceClickRadius = value; }
        }
        public string TypeOfClick
        {
            get { return _typeOfClick; }
            set { _typeOfClick = value; }
        }
        public int CommonTimerMultiple
        {
            get { return _commonTimerMultiple; }
            set { _commonTimerMultiple = value; }
        }
        #endregion

        #region Constructor
        public AlgorithmPercentageCalculating(int commonTimerMultiple, int radius, ICommonTimer timer, IPointerDeviceDriver pointerDeviceDriver, string typeOfClick, AutoClickCircleControl autoClickCircleControl)
        {
            TypeOfClick = typeOfClick;
            AutoClickCircleControl = autoClickCircleControl;
            AreaOfReferenceClickRadius = radius;
            ClickInProcess = true;
            _pointerDeviceDriver = pointerDeviceDriver;
            ReferencialPointerPosition = new Point(_pointerDeviceDriver.CurrentPointerXLocation
                                                    , _pointerDeviceDriver.CurrentPointerYLocation);

            _listOfPointerPositionOutOfAreaClick = new List<int>();
            _commonTimer = timer;
            _commonTimerMultiple = commonTimerMultiple;
        }
        #endregion

        #region Control of click
        /// <summary>
        /// Method called each periods of CommonTimer.
        /// Method that launch ClickTick's method
        /// called each time a Timer.Tick event is sent by the CommonTimer.
        /// launches ClickTick's method
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Timer_Tick(object sender, EventArgs e)
        {
            CountNumberOfCommonTimerTicks++;
            if (CountNumberOfCommonTimerTicks >= _commonTimerMultiple && TypeOfClick != "noClick")
            {
                Click_Tick();
                CountNumberOfCommonTimerTicks = 0;
            }
        }
        /// <summary>
        /// Method that :
        /// - Launch an autoclick if the specifics conditions are OK
        /// - Set a new referencial position if the specifics conditions are not OK
        /// - Control the circle reference for click
        /// </summary>
        public void Click_Tick()
        {
            _accuracyOfPointerMovements = -1; 
            //Stop saving new mouse points
            _pointerDeviceDriver.PointerMove -= new PointerDeviceEventHandler(onMouseMove);
            if (_listOfPointerPositionOutOfAreaClick.Count != 0)
            {
                // calculate the percentage of points that are within the circle of acceptance                
                foreach (int distance in _listOfPointerPositionOutOfAreaClick)
                {
                    if (distance > AreaOfReferenceClickRadius)
                        _numberOfPointerPositionsOutOfAreaClick++;
                }
                _accuracyOfPointerMovements = 100 - ((_numberOfPointerPositionsOutOfAreaClick * 100) / (_listOfPointerPositionOutOfAreaClick.Count));
            }

            //Clicks if this is needed or calcuate the new referencial position
            //updates the circle position
            //specifies if an autoclick is in process
            if (_accuracyOfPointerMovements >= 80 && ClickInProcess)
            {
                LaunchClick(TypeOfClick, _pointerDeviceDriver);
                ClickInProcess = false;
            }
            else if (_accuracyOfPointerMovements != -1)
            {
                ReferencialPointerPosition = new Point(_pointerDeviceDriver.CurrentPointerXLocation
                                                        , _pointerDeviceDriver.CurrentPointerYLocation);
                ClickInProcess = true;
                _autoClickCircleControl.UpdateCirclePosition(ReferencialPointerPosition);
            }
            else
                ClickInProcess = false;

            // Clear the pointList and start saving new points again
            _listOfPointerPositionOutOfAreaClick.Clear();
            _numberOfPointerPositionsOutOfAreaClick = 0;
            _pointerDeviceDriver.PointerMove += new PointerDeviceEventHandler(onMouseMove);
        }
        /// <summary>
        /// Method called each time a PointerMove is sent by the PointerDeviceDriver.
        /// It adds the current point to the pointList.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void onMouseMove(object sender, PointerDeviceEventArgs e)
        {
            //Calculate the distance between the referencial mouse position and the actual mouse position
            //and save it in _distancesList
            _distanceBetweenPointerAndReference.X = Math.Abs(ReferencialPointerPosition.X - e.X);
            _distanceBetweenPointerAndReference.Y = Math.Abs(ReferencialPointerPosition.Y - e.Y);
            _pointerDistanceFromReferance = Math.Sqrt((int)Math.Pow(_distanceBetweenPointerAndReference.X, 2) + (int)Math.Pow(_distanceBetweenPointerAndReference.Y, 2));
            _listOfPointerPositionOutOfAreaClick.Add((int)_pointerDistanceFromReferance);
        }
        #endregion

        #region IAutoClickAlgorithm Membres
        public void Start()
        {
            _pointerDeviceDriver.PointerMove += new PointerDeviceEventHandler(onMouseMove);
            _commonTimer.Tick += new EventHandler(Timer_Tick);
        }
        public void Stop()
        {
            _commonTimer.Tick -= new EventHandler(Timer_Tick);
            _pointerDeviceDriver.PointerMove -= new PointerDeviceEventHandler(onMouseMove);
        }
        #endregion
    }
}
